/*****************************************************
*                                                                          
*               COPYRIGHT (c) 2015-2017 Broadlink Corporation              
*                         All Rights Reserved                              
*                                                                          
* The source code contained or described herein and all documents          
* related to the source code ("Material") are owned by Broadlink         
* Corporation or its licensors.  Title to the Material remains             
* with Broadlink Corporation or its suppliers and licensors.               
*                                                                          
* The Material is protected by worldwide copyright and trade secret        
* laws and treaty provisions. No part of the Material may be used,         
* copied, reproduced, modified, published, uploaded, posted, transmitted,  
* distributed, or disclosed in any way except in accordance with the       
* applicable license agreement.                                            
*                                                                          
* No license under any patent, copyright, trade secret or other            
* intellectual property right is granted to or conferred upon you by       
* disclosure or delivery of the Materials, either expressly, by            
* implication, inducement, estoppel, except in accordance with the         
* applicable license agreement.                                            
*                                                                          
* Unless otherwise agreed by Broadlink in writing, you may not remove or   
* alter this notice or any other notice embedded in Materials by Broadlink 
* or Broadlink's suppliers or licensors in any way.                        
*                                                                          
** Author: shengda.huang
** Date: 2018-09-17
*
*****************************************************/

/***** Include files ********************************/
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include "bipbuffer.h"

/***** Defines **************************************/
#define get_data_byte_count() \
            (_this->partitionASize + _this->partitionBSize)
#define get_partition_b_freespace() \
            (_this->partitionAIndex - _this->partitionBIndex - _this->partitionBSize)
#define get_partition_a_freespace() \
            (_this->bufferSize - _this->partitionAIndex - _this->partitionASize)

/***** Define structure *****************************/

/***** Define constant ******************************/

/***** Define global ********************************/

/***** Define Prototype of functions ****************/

/**
 * @brief
 * @param  None
 * @retval None
 */
int bipbuffer_construct(struct bipbuf* _this, unsigned int size)
{
    memset(_this, 0x0, sizeof(struct bipbuf));

    _this->bufferSize = size - offsetof(struct bipbuf, buffer);
    memset(_this->buffer, 0x0, _this->bufferSize);
    return 0;
}

void bipbuffer_destroy(struct bipbuf* _this)
{
    memset(_this, 0x0, sizeof(struct bipbuf));
}

void* bipbuffer_reserve(struct bipbuf* _this, unsigned int size)
{
    unsigned int free_space = 0;

    /*
     *  Allways allocate new space from partition B if it exist.
     */
    if( _this->partitionBSize != 0 )
    {
        free_space = get_partition_b_freespace();

        if( free_space == 0 || free_space < size )
        {
            return NULL;
        }

        _this->reservedSize = size;
        _this->reservedIndex = _this->partitionBIndex + _this->partitionBSize;
       
        return &_this->buffer[_this->reservedIndex];
    }
    else
    {
        /*
         *  Partition B does not exist, allocate space from partition A
         *  if it there is enough space, otherwise try from partition B.
         */ 
        free_space = get_partition_a_freespace();
        
        if( free_space >= size )
        {
            if( free_space == 0 )
            {
                return NULL;
            }
           
            _this->reservedSize = size;
            _this->reservedIndex = _this->partitionAIndex + _this->partitionASize;
            
            return &_this->buffer[_this->reservedIndex];
        }
        else
        {
            /* if partitionAIndex is in the middle, and partitionASize is equal to 0 */
            if(    0 < _this->partitionAIndex \
                && 0 == _this->partitionASize \
                && size <= _this->bufferSize)
            {
                _this->reservedSize = size;
                _this->partitionAIndex = 0;
                _this->reservedIndex = 0;

                 return &_this->buffer[0]; // return starting point of buffer
            }
            else if( _this->partitionAIndex == 0 \
                || _this->partitionAIndex < size \
                || size > _this->bufferSize)
            {
                return NULL;
            }

            _this->reservedSize = size;
            _this->reservedIndex = 0;
           
            return &_this->buffer[0];
        }
    }
}

void bipbuffer_commit(struct bipbuf* _this, unsigned int size)
{
    if( size == 0 )
    {
        /*
         *  Decommit any reservation
         */
        _this->reservedSize = 0;
        _this->reservedIndex = 0;
    
        return;
    }

    /*
     *  Prevent trying to commit more space than asked for.
     */
    if( size > _this->reservedSize )
    {
        size = _this->reservedSize;
    }

    /*
     *  When no regions being used, region A is reserved.
     */
    if( _this->partitionASize == 0 && _this->partitionBSize == 0 )
    {
        _this->partitionAIndex = _this->reservedIndex;
        _this->partitionASize = size;

        _this->reservedIndex = 0;
        _this->reservedSize = 0;
        
        return;
    }

    if( _this->reservedIndex == (_this->partitionASize + _this->partitionAIndex) )
    {
        _this->partitionASize += size;
    }
    else
    {
        _this->partitionBSize += size;
    }

    _this->reservedIndex = 0;
    _this->reservedSize = 0;
}

void* bipbuffer_get(struct bipbuf* _this, unsigned int* countinousBlockSizePtr)
{
    if( _this->partitionASize == 0 )
    {
        *countinousBlockSizePtr = 0;       
        return NULL;
    }

    *countinousBlockSizePtr = _this->partitionASize;
        
    return &_this->buffer[_this->partitionAIndex];
}

void bipbuffer_decommit(struct bipbuf* _this, unsigned int size)
{
    if( size >= _this->partitionASize &&
        _this->partitionBSize != 0 )
    {
        _this->partitionAIndex = _this->partitionBIndex;
        _this->partitionASize = _this->partitionBSize;
        
        _this->partitionBIndex = 0;
        _this->partitionBSize = 0;
    }
    else
    {
        _this->partitionASize -= size;
        _this->partitionAIndex += size;
    }
}

unsigned int bipbuffer_usedsize(struct bipbuf* _this)
{
    return get_data_byte_count(); 
}

/* End of file */
